# -*- coding: utf-8 -*-
"""
Created on Thu Jun 13 17:32:43 2019
Created on Python3.6.8
@author:
    liwancai
    QQ:248411282
"""
# %% 文件夹
# import tkinter as tk
# from tkinter import filedialog
from kds_util.user_logbook import user_log
import shutil,os,configparser,threading,json,csv
# ==============================================================================
def Mkdir(path):
    '''创建文件夹'''
    try:
        isExists = os.path.exists(path)  # 判断文件路径是否存在
        if not isExists:
            os.makedirs(path)
            user_log.info("\n|■|文件夹==>> %s |■|创建成功|■|" % path)
        else:
            user_log.info("\n|■|文件夹==>> %s |■|已经存在|■|" % path)
    except Exception as exception:
        user_log.error(str(exception))
# ==============================================================================
def ReName(srcFile,dstFile):
    try:
        os.rename(srcFile,dstFile)
        user_log.info(f"\n|■|源文件:{srcFile}==>>目标:{dstFile} 重命名成功|■|")
    except Exception as exception:
        user_log.error(str(exception))
# ==============================================================================
def Del_P(path):
    '''删除文件夹'''
    try:
        shutil.rmtree(path)
        user_log.info('\n|■|文件夹: %s |■|删除成功|■|' % path)
    except Exception as exception:
        user_log.error(str(exception))
def Del_F(filepath):
    '''删除文件'''
    try:
        if os.path.exists(filepath):  # 如果文件存在 # 删除文件,可使用以下两种方法。
            os.remove(filepath)
            # os.unlink(path)
            user_log.info('\n|■|文件: %s |■|删除成功|■|' % filepath)
        else:
            user_log.info('\n|■|文件: %s |■|不存在|■|' % filepath)  # 则返回文件不存在
    except Exception as exception:
        user_log.error(str(exception))
# ==============================================================================
def Filesdirs(path):
    '''文件路径'''
    return [os.path.join(path, info) for info in os.listdir(path)]
def Filesname(path):
    '''路径下文件夹名称'''
    return [info for info in os.listdir(path) if '.' not in info]
# ==============================================================================
def WriteFile(data, filename, path, arge="a"):
    '''save text'''
    try:
        Mkdir(path)
        File = open(path + filename, arge)  # 输出的txt文件路径
        File.write(data)
        File.close()
    except Exception as exception:
        user_log.error(str(exception))
def ReadFile(filepath):
    '''读取任意文件'''
    with open(filepath, "r") as file: return file.read()
# //////////////////////////////////////////////////////////////////////////////
def Save_Json(data, path='./', filename='filename',mode='w'):
    Mkdir(path)
    (open('%s%s.json' % (path, filename), mode)).write(f"\n{json.dumps(data)}\n")
# //////////////////////////////////////////////////////////////////////////////
def Read_Json(filename,path):
    return json.loads((open('%s%s.json' % (path,filename), 'r')).read())
# //////////////////////////////////////////////////////////////////////////////
def Read_Csv(path, encoding='gb18030', chunksize=None,nrows=None, index_col=0,**arge):
    import pandas as pd
    return pd.read_csv(path,index_col=index_col, encoding=encoding, nrows=nrows,chunksize=chunksize, engine='python',**arge)

# //////////////////////////////////////////////////////////////////////////////
def Read_xls(path):
    import pandas as pd
    return pd.read_excel(path)
# //////////////////////////////////////////////////////////////////////////////
def Save_csv(df, filepath, encoding='gb18030', mode=None):
    '''pandas df to csv'''
    import os
    try:
        isExists = os.path.exists(filepath)
        if mode == None:
            if not isExists:
                mode, header = 'w', True
                df.to_csv(filepath, mode=mode, header=header, encoding=encoding)
                user_log.info('\n|■|文件: %s |■|保存成功|■|' % filepath)
            else:
                mode, header = 'a', False
                df.to_csv(filepath, mode=mode, header=header, encoding=encoding)
                user_log.info('\n|■|文件: %s |■|追加成功|■|' % filepath)
        else:
            header = False
            df.to_csv(filepath, mode=mode, header=header, encoding=encoding)
            user_log.info('\n|■|文件: %s |■|追加成功|■|' % filepath)
    except Exception as exception:
        user_log.error(str(exception))
# //////////////////////////////////////////////////////////////////////////////
class TodoCsv:
    '''
    a:附加写,不可读。
    a+:附加读写。追加写。
    r:只读,最常用。不创建,不存在会报错。（读二进制文件可能会读取不全）
    rb:只读。二进制文件按二进制位进行读取。不创建,不存在会报错。
    rt:只读。文本文件用二进制读取。不创建,不存在会报错。
    r+:可读写。覆盖写。不创建,不存在会报错。
    w:只写。存在则覆盖。不存在则创建。
    w+:可读写。存在则覆盖。不存在则创建。
    '''
    def __init__(self,csvname,filepath,act="a+",encoding='gb18030',newline=''):
        Mkdir(filepath)
        self.pathfile=f"{filepath}{csvname}.csv"
        self.csvfile=open(self.pathfile,mode=act,encoding=encoding,newline=newline)
        self.csvfile_w=csv.writer(self.csvfile)
    def writer(self,csvdata):
        self.csvfile_w.writerow(csvdata)
    def flush(self):
        self.csvfile.flush()
    def close(self):
        self.csvfile.close()
    def GetSize(self):
        return os.path.getsize(self.pathfile) 
# //////////////////////////////////////////////////////////////////////////////

class TK_file:
    '''文件夹路径操作'''
    def __init__(self):
        self.root = tk.Tk()   # 创建一个Tkinter.Tk()实例
        self.root.withdraw()  # 将Tkinter.Tk()实例隐藏
    @classmethod
    def OpenFile(cls,title="请选择一个文件",filetypes=[('All Files', ' *')]):
        return filedialog.askopenfilename(title=title,filetypes=filetypes,defaultextension='.tif', multiple=True)
    @classmethod
    def OpenFiles(cls,title="请选择多个文件",filetypes=[('All Files', ' *')]):
        return filedialog.askopenfilename(title=title,filetypes=filetypes)
    @classmethod
    def SaveFile(cls,title="请选择文件存储路径",filetypes=[('All Files', ' *')]):
        return filedialog.askopenfilename(title=title,filetypes=filetypes, defaultextension='.tif')
    @classmethod
    def DirPath(cls,):
        return filedialog.askdirectory(title='选择目标文件夹')
    @classmethod
    def SrePath(cls,):
        return filedialog.askdirectory(title='选择源始文件夹')
    
def SreTarDIR():  # 窗口选择文件夹路径
    '''源文件夹 目标文件夹选择'''
    try:
        root = tk.Tk()
        root.withdraw()
        ###请选择源始文件夹
        user_log.info('\n|■|请选择源始文件夹|■|')
        source_dir = filedialog.askdirectory(title='选择源始文件夹')
        user_log.info('\n|■|<<源始文件夹: %s >>|■|' % source_dir)
        ###请选择目标文件夹
        user_log.info('\n|■|请选择目标文件夹|■|')
        target_dir = filedialog.askdirectory(title='选择目标文件夹')
        user_log.info('\n|■|<<目标文件夹: %s >>|■|' % target_dir)
        return source_dir,target_dir
    except Exception as exception:
        err_log.error(str(exception))
        return None, None
# ==============================================================================
def ImgToBase64(filename,path):
    '''
    图片转base64
    '''
    import io
    from PIL import Image
    img=Image.open(f"{path}{filename}")
    try:r,g,b,a = img.split()  
    except: r,g,b = img.split() 
    img = Image.merge('RGB',(r,g,b))    
    output=io.BytesIO()
    img.save(output, format='JPEG')#
    strimg=Base64_img(output.getvalue())
    return  strimg
def Base64_img(img):
    '''base64编码'''
    import base64
    return str(base64.b64encode(img))[1:]
def Base64ImgSave(name,img_str,path="./temp/"):
    '''base64转图片'''
    import base64,time
    Mkdir(path)
    filename=f'{path}{name}{str(time.time())}.png'
    with open(filename, 'wb') as imgfile:
        imgfile.write(base64.b64decode(img_str))#base64解码
        imgfile.close()
        return os.path.abspath(filename)#发出文件绝对路径(用于微信推送)
# ============================================================================== 
def RunCMD(cmd_string):
    """
    执行cmd命令，并得到执行后的返回值，python调试界面输出返回值
    :param cmd_string: cmd命令，如：'adb devices'
    :return:
    """
    import subprocess
    print('运行cmd指令：{}'.format(cmd_string))
    return subprocess.Popen(cmd_string, shell=True, stdout=None, stderr=None).wait()

# ============================================================================== 
def is_The_file(filename, Endlist=['.png', '.jpg', 'tif', 'TIF', '.jpeg', '.JPG', '.JPEG', '.PNG', '.gif', '.GIF']):
    '''
    判断指定后缀名文件:如
    图片 ['.png', '.jpg','tif','TIF', '.jpeg', '.JPG', '.JPEG', '.PNG','.gif','.GIF']
    文档 ['.pdf','.PDF','.csv','.CSV','.doc','.docx','.xls','.DOC','.DOCX','.XLS','.XLSX','.xlsx']
    '''
    return any(filename.endswith(extension) for extension in Endlist)
# ==============================================================================
def EndWithPath(path, endlist):
    '''获取指定后缀名的文件路径'''
    try:
        list_name = []
        def Scan_File(list_name, path):
            for file in os.listdir(path):
                file_path = os.path.join(path, file)
                file_path = file_path.replace('\\', '/')
                if os.path.isdir(file_path):
                    Scan_File(list_name, file_path)
                else:
                    list_name.append(file_path)
        Scan_File(list_name, path)
        return [x for x in list_name if is_The_file(x, endlist)]
    except Exception as exception:
        err_log.error(str(exception))
        return []
# ==============================================================================
#%% 配置文件操作
class JsonConf:
    def __init__(self, file_name):
        self.file_name = file_name
        self._data = self.load_file(file_name)
    def get(self,arge):
        return self._data[arge]
    def load_file(self, file_name):
        if not os.path.exists(file_name):
            return {}
        with open(file_name, encoding="utf-8") as json_file:
            try:
                data = json.load(json_file)
            except Exception as e:
                print(e)
                data = {}
        return data
class ConfigMgr:
    '''配置文件'''
    _instance_lock = threading.Lock()
    def __init__(self):
        pass
    def __new__(cls, *args, **kwargs):
        if not hasattr(ConfigMgr, "_instance"):
            with ConfigMgr._instance_lock:
                if not hasattr(ConfigMgr, "_instance"):
                    ConfigMgr._instance = object.__new__(cls)
                    ConfigMgr._instance.load_conf()
        return ConfigMgr._instance
    def load_conf(self):
        a0 = JsonConf("../../../etc/global_config.json")
        a = JsonConf("../../etc/config.json")
        b = JsonConf("etc/config.json")
        # 不能放在init中,原因是每调用一次ConfigMgr._instance就会调用一次init
        tmp = dict(a0.get(), **a.get())
        self._data = dict(tmp, **b.get())
        print(json.dumps(self._data, indent=2, ensure_ascii=False))
    @staticmethod
    def get(*args):
        length = len(args)
        if length == 1:
            return ConfigMgr._instance._data[args[0]]
        elif length == 2:
            return ConfigMgr._instance._data[args[0]][args[1]]
        elif length == 3:
            return ConfigMgr._instance._data[args[0]][args[1]][args[2]]
    @staticmethod
    def has_key(*args):
        length = len(args)
        data = ConfigMgr._instance._data
        if length == 1:
            return args[0] in data
        elif length == 2:
            return args[0] in data and args[1] in data[args[0]]
        elif length == 3:
            return args[0] in data and args[1] in data[args[0]] and args[2] in data[args[0]][args[1]]
# ==============================================================================
#%% 配置文件操作
class ConFigDB:
    def __init__(self,PATH="./Datas/ConFigs/",filename=None):
        '''若初始化指定文件名则默认初始化文件名'''
        self.CFG =configparser.ConfigParser()
        self.PATH=PATH
        self.filename=filename
        Mkdir(self.PATH)
    def Read(self,filename=None):
        '''
        从文件读取
        '''
        filename=self.PATH+self.filename+".ini" if filename==None else self.PATH+filename+".ini"
        self.CFG.read(filename,encoding="utf-8")  # 读取文件
    def Save(self,filename=None):
        '''保存到文件'''
        filename=self.PATH+self.filename+".ini" if filename==None else self.PATH+filename+".ini"
        file= open(filename, mode="w",encoding="utf-8")
        self.CFG.write(file)
        file.flush()
        file.close()
    def GetSection(self):
        '''获取章节列表'''
        return self.CFG.sections()
    def GetOption(self,sections):
        '''获取章节元素列表'''
        return self.CFG.options(sections)   
    def GetDict(self,sections):
        '''获取元组'''
        return self.DictJson(self.CFG.items(sections))
    def GetSctItem(self,sections,item):
        '''获取章节元素值'''
        return self.CFG.get(sections,item)
    def Add_section(self,section,jsons):
        '''新增章节并赋值'''
        try:
            self.CFG.add_section(section)
            self.CFG[section]=jsons
        except Exception as exception:
            err_log.error(str(exception))
            return []
    def Add_Item(self,sections,item,arge):
        '''新增元素并赋值'''
        self.CFG[sections][item] = arge
    def RmvSection(self,sections):
        '''删除章节'''
        self.CFG.remove_section(sections)
    def RmvOption(self,sections,item):
        '''删除元素'''
        self.CFG.remove_option(sections,item)
    def Set(self,sections,item,arge):
        '''修改元素'''
        self.CFG.set(sections,item,arge)
    @classmethod
    def EVALI(cls,STData):
        '''字符串转列表字典'''
        for i in ["[" , "]" , "{" , "}" ]:
            if i in STData:STData=eval(STData)
        return STData
    @classmethod
    def GETKEY(cls,JSData):
        '''获取Json的KEY'''
        try:
            return list(JSData.keys())
        except:return []
    @classmethod
    def Float(cls,strs):
        '''转换成浮点数'''
        try:return float(strs)
        except:return strs
    @classmethod
    def JsonSTF(cls,JSData):
        for key in ConFigDB.GETKEY(JSData):JSData[key]=ConFigDB.Float(JSData[key])
        return JSData
    
    @classmethod
    def DictJson(cls,data):
        '''批量配置文件字符串列表字典 转列表字典'''
        return {data[i][0]:ConFigDB.EVALI(data[i][1]) for i in range(len(data))}
# ==============================================================================
#%% 文件夹目录树
import re
from pathlib import Path
from pathlib import WindowsPath
from typing  import Optional,List
# ==============================================================================
class DirectionTree():
    def __init__(self,
                 direction_name: str = 'WorkingDirection',
                 direction_path: str = '.',
                 ignore_list: Optional[List[str]] = ['\.git', '__pycache__', 'test.+', 'venv', '.+\.whl', '\.idea',
                                                     '.+\.jpg', '.+\.png', 'image', 'css', 'admin', 'tool.py',
                                                     'db.sqlite3']):
        self.owner: WindowsPath = Path(direction_path)
        self.tree: str = direction_name + '/\n'
        self.ignore_list = ignore_list
        if ignore_list is None:
            self.ignore_list = []
        self.direction_ergodic(path_object=self.owner, n=0)
    def tree_add(self, path_object: WindowsPath, n=0, last=False):
        if n > 0:
            if last:
                self.tree += '│' + ('    │' * (n - 1)) + '    └────' + path_object.name
            else:
                self.tree += '│' + ('    │' * (n - 1)) + '    ├────' + path_object.name
        else:
            if last:
                self.tree += '└' + ('──' * 2) + path_object.name
            else:
                self.tree += '├' + ('──' * 2) + path_object.name
        if path_object.is_file():
            self.tree += '\n'
            return False
        elif path_object.is_dir():
            self.tree += '/\n'
            return True
    def filter_file(self, file):
        for item in self.ignore_list:
            if re.fullmatch(item, file.name):
                return False
        return True
    def direction_ergodic(self, path_object: WindowsPath, n=0):
        dir_file: list = list(path_object.iterdir())
        dir_file.sort(key=lambda x: x.name.lower())
        dir_file = [f for f in filter(self.filter_file, dir_file)]
        for i, item in enumerate(dir_file):
            if i + 1 == len(dir_file):
                if self.tree_add(item, n, last=True):
                    self.direction_ergodic(item, n + 1)
            else:
                if self.tree_add(item, n, last=False):
                    self.direction_ergodic(item, n + 1)
# ==============================================================================
def PathTree(path, Write=False, filename='pathtree', savepath='./', arge="a"):  # 文件目录树
    PathTree = DirectionTree(direction_path=path).tree
    user_log.info(PathTree)
    if Write: WriteFile(PathTree, filename, savepath, arge)

# %% 终结
if __name__ == '__main__':
    EndWithPath('../../', [".py"])
    SreTarDIR()
